﻿using MongoDB.Bson;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;

namespace anydata.Extensions
{
    public class ObjectIdConverter : JsonConverter<ObjectId>
    {
        public override ObjectId ReadJson(JsonReader reader, Type objectType, ObjectId existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            if (hasExistingValue)
            {
                existingValue = ObjectId.Parse(JsonSettings.Json.Deserialize<string>(reader));
            }
            return existingValue;
        }

        public override void WriteJson(JsonWriter writer, ObjectId value, JsonSerializer serializer)
        {
            writer.WriteValue(value.ToString());
        }
    }

    public class DateTimeConverter : JsonConverter<DateTime>
    {
        public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            var dtStr = JsonSettings.Json.Deserialize<string>(reader);
            return DateTime.TryParse(dtStr, out var date) ? date : existingValue.ToLocalTime();
        }

        public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)
        {
            if (value.Kind == DateTimeKind.Utc)
            {
                value = value.ToLocalTime();
            }
            JsonSettings.Json.Serialize(writer, value.ToString(JsonSettings.Json.DateFormatString));
        }
    }

    public class BsonDocumentConverter : JsonConverter<BsonDocument>
    {
        public override BsonDocument ReadJson(JsonReader reader, Type objectType, BsonDocument? existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            if (hasExistingValue)
            {
                existingValue = new BsonDocument(JsonSettings.Json.Deserialize<IDictionary<string, object>>(reader));
                return existingValue;
            }
            return new BsonDocument();
        }

        public override void WriteJson(JsonWriter writer, BsonDocument? value, JsonSerializer serializer)
        {
            JsonSettings.Json.Serialize(writer, value?.ToDictionary());
        }
    }

    public static class JsonSettings
    {
        public static JsonSerializerSettings Settings = new JsonSerializerSettings().Setting();
        public static JsonSerializer Json = JsonSerializer.CreateDefault(Settings);
        public static JsonMergeSettings MergeUnion = new JsonMergeSettings()
        {
            MergeArrayHandling = MergeArrayHandling.Union,
            MergeNullValueHandling = MergeNullValueHandling.Ignore
        };

        public static JsonMergeSettings MergeReplace = new JsonMergeSettings()
        {
            MergeArrayHandling = MergeArrayHandling.Replace,
            MergeNullValueHandling = MergeNullValueHandling.Ignore
        };

        public static JsonMergeSettings MergeConcat = new JsonMergeSettings()
        {
            MergeArrayHandling = MergeArrayHandling.Concat,
            MergeNullValueHandling = MergeNullValueHandling.Ignore
        };

        public static JsonMergeSettings Merge = new JsonMergeSettings()
        {
            MergeArrayHandling = MergeArrayHandling.Merge,
            MergeNullValueHandling = MergeNullValueHandling.Ignore
        };

        public static string ToJson(this object data)
        {
            return JsonConvert.SerializeObject(data, Settings);
        }

        public static T? ToObject<T>(this object data)
        {
            return JsonConvert.DeserializeObject<T>(data.ToJson(), Settings);
        }

        public static T? ToObject<T>(this string data)
        {
            return JsonConvert.DeserializeObject<T>(data, Settings);
        }

        public static BsonDocument ToBson(this object data)
        {
            return BsonDocument.Parse(data.ToJson());
        }
    }

    public static class JsonConvertExtrensions
    {
        public static JsonSerializerSettings Setting(this JsonSerializerSettings settings)
        {
            settings.Formatting = Formatting.None;
            settings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
            settings.DateFormatString = "yyyy-MM-dd HH:mm:ss.fff";
            settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            settings.ContractResolver = new DefaultContractResolver();
            settings.Converters.Add(new ObjectIdConverter());
            settings.Converters.Add(new DateTimeConverter());
            settings.Converters.Add(new BsonDocumentConverter());
            return settings;
        }

        public static Task WriteToJsonAsync(this HttpResponse response, object anyObject)
        {
            response.StatusCode = 200;
            response.ContentType = "application/json";
            return response.WriteAsync(anyObject.ToJson());
        }
    }
}
